/***************************************************************************
 *   Ants - library                                                        *
 *                                                                         *
 *   Copyright (C) 2008 by kuerant                                         *
 *                         <kuerant@gmail.com>                             *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Library General Public License as       *
 *   published by the Free Software Foundation; either version 2 of the    *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 *   This library is distributed in the hope that it will be usefull, but  *
 *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      *
 *   Library General Public License for more details.                      *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; if not, write to the Free Software   *
 *   Foundation, Inc., 59 Temple Place - Suite 330, Boston,                *
 *   MA 02111-1307, USA                                                    *
 ***************************************************************************/


#include    <signal.h>

#include    "common/packet.hpp"

#include    "ants/switcher.hpp"

using namespace std;
using namespace ant;

namespace   ants
{
    
////////////////////////////////////////////////////////////////////////

Switcher::Switcher()
    :   m_pLogger(NULL), m_loop(NULL), m_pIdle(NULL)
{
}

Switcher::~Switcher()
{
    for (udpwatchers_type::iterator  it = m_udpwatchers.begin(); it != m_udpwatchers.end(); ++it ) {
        int     iPort = it->first;
        UdpWatcher*  pWatcher = it->second;
        
        delete pWatcher;
    }
    m_udpwatchers.clear();
}

////////////////////////////////////////////////////////////////////////

void
Switcher::run(void)
{
    vector<ev::sig *>   signalWatchers;
    
    ev::sig*    pSignalWatcher = NULL;
    
    int     iSignals[] = { SIGHUP, SIGINT, SIGPIPE, SIGTERM, 0 };
    
    for (int i=0; iSignals[i] > 0; ++i) {
        pSignalWatcher = new ev::sig();
        pSignalWatcher->set<Switcher, &Switcher::callback_Signal>(this);
        pSignalWatcher->start(iSignals[i]);

        signalWatchers.push_back(pSignalWatcher);
    }

    m_pIdle = new ev::idle();
    m_pIdle->set<Switcher, &Switcher::callback_idle>(this);
    m_pIdle->start();
            
    m_loop = ev_default_loop(0);
    ev_loop(m_loop, 0);

    m_pIdle->stop();
    delete m_pIdle;
    m_pIdle = NULL;
    
    for (size_t i=0; i<signalWatchers.size(); ++i) {
        pSignalWatcher = signalWatchers[i];
        
        delete pSignalWatcher;
    }
    
    logger().error("Not Implemented Completely, yet!!!");
}

////////////////////////////////////////////////////////////////////////
// watch methods
void
Switcher::watchUDP(int port)
{
    udpwatchers_type::iterator   result = m_udpwatchers.find(port);
    if ( m_udpwatchers.end() != result ) {
        return;
    }
    
    UdpWatcher* pWatcher = new UdpWatcher(port);
    pWatcher->set<Switcher, &Switcher::callback_UDP>(this);
    pWatcher->start(pWatcher->descriptor(), ev::READ);
    
    m_udpwatchers[port] = pWatcher;
}


////////////////////////////////////////////////////////////////////////
// event callback methods
void    
Switcher::callback_UDP(ev::io& watcher, int revent)
{
    UdpWatcher* pWatcher = static_cast<UdpWatcher *>(&watcher);
    
    if (debugable()) logger().debug(__FILE__, __LINE__, "fd=%d revent=0x%04X (%s) ==== ENTER ====", pWatcher->descriptor(), revent, evname(revent));

    if (revent & ev::READ) {    
        string  strPacketIncome;
        zsocket::Address    addressIncome;
        
        try {
            pWatcher->recv(addressIncome, strPacketIncome);
        }
        catch (ant::Exception& ex)
        {
            logger().error("%s() Exception : [%d] %s", __func__, ex.getCode(), ex.what());
            return;
        }
        Packet  packetIncome(strPacketIncome);
     
        if (debugable()) logger().debug(__FILE__, __LINE__, "IN Packet(%s)", packetIncome.toString().c_str());
        
        if ( packetIncome.isDataPacket() ) {
            // send SYN 
            pWatcher->engageSynToSend(addressIncome, packetIncome.getSerial());
            
            // enable write
            pWatcher->start(pWatcher->descriptor(), ev::WRITE);
            
            // enqueue income packet to schedule
            void*   pvPacket = getPacketBuffer();
            if (NULL == pvPacket) {
                logger().error( "No more packet buffer available." );
            }
            Packet*     pPacket = static_cast< Packet* >(pvPacket);
            
            *pPacket = packetIncome;
            enqueueRequest( pPacket );
        }
        else {
            // non-DATA packet
        }
    }
    else if (revent & ev::WRITE) {
        pWatcher->flush();
        
        pWatcher->stop();
        pWatcher->start(pWatcher->descriptor(), ev::READ);
        logger().error(__FILE__, __LINE__, "%s() To Be Implemented.", __func__);
    }

    if (debugable()) logger().debug(__FILE__, __LINE__, "fd=%d revent=0x%04X (%s) ==== EXIT  ====", pWatcher->descriptor(), revent, evname(revent));

    // enable idle-watcher to do some routine
    m_pIdle->start();
}

void
Switcher::callback_idle(ev::idle& watcher, int revent)
{
    watcher.stop();
}

void    
Switcher::callback_Signal(ev::sig& watcher, int revent)
{
    int     iSignal = watcher.signum;
    
    if ( (2 == iSignal) || (15 == iSignal) ) {
        logger().error("Catch #%d signal, will stop ...", iSignal);
        logger().info("Catch #%d signal, will stop ...", iSignal);
        ev_unloop(m_loop, EVUNLOOP_ALL);
    }
}

////////////////////////////////////////////////////////////////////////
// helper methods
void
Switcher::setLogger(help::Logger* pLogger)
{
    if (NULL != pLogger) {
        m_pLogger = pLogger;
    }
}

const char*
Switcher::evname(int event)
{
    static  const char*     s_pszEventName[] = {
        "EV_NONE",
        "EV_READ",
        "EV_WRITE",
        "EV_UNKNOWN"
    };
    int     iUnknown = 3;
    
    switch(event) {
    case    ev::READ    :
        return  s_pszEventName[1];
    case    ev::WRITE   :
        return  s_pszEventName[2];
    }
    
    return  s_pszEventName[ iUnknown ];
}

void*
Switcher::getPacketBuffer(void)
{
    Packet* pPacket = new Packet();
    
    return  pPacket;
}

void
Switcher::releasePacketBuffer(void*& pvPacket)
{
    Packet* pPacket = static_cast<Packet*>(pvPacket);
    delete pPacket;
    
    pvPacket = NULL;
}

}   // namespace ants
